#include "cobjparser.h"

#include <buola/algorithm/split.h>

#include <buola/gl.h>
#include <buola/scene/cmesh.h>
#include <buola/scene/cvertexdata.h>
#include <buola/scene/cindexdata.h>

namespace buola { namespace scene { namespace import {

COBJParser::COBJParser()
    :   mCurGroup(nullptr)
    ,   mCurSmoothGroup(nullptr)
    ,   mCurMaterial(nullptr)
{

}

COBJParser::~COBJParser()
{
    for(int i=0;i<mGroups.size();i++)
        delete mGroups[i];
    for(int i=0;i<mSmoothGroups.size();i++)
        delete mSmoothGroups[i];
    for(int i=0;i<mMaterials.size();i++)
        delete mMaterials[i];
}

void COBJParser::ParseV(const std::vector<std::string> &pV)
{
    std::vector<mat::CVec3d> *lVector=nullptr;

    if(pV[0]=="v")
    {
        lVector=&mPosition;
    }
    else if(pV[0]=="vt")
    {
        lVector=&mTexCoord;
    }
    else if(pV[0]=="vn")
    {
        lVector=&mNormal;
    }
    else
    {
        msg_warn() << "unknown vertex type " << pV[0] << "\n";
    }

    mat::CVec3d lNew;

    if(pV.size()<2||pV.size()>4)
    {
        msg_warn() << "wrong number of parameters to v while parsing obj\n";
        return;
    }

    lNew.x()=to_<double>(pV[1]);

    if(pV.size()>=3)
        lNew.y()=to_<double>(pV[2]);
    else
        lNew.y()=0.0;
    if(pV.size()>=4)
        lNew.z()=to_<double>(pV[3]);
    else
        lNew.z()=0.0;

    lVector->push_back(lNew);
}

void COBJParser::ParseF(const std::vector<std::string> &pV)
{
    if(pV.size()<4)
    {
        msg_warn() << "shape with less than 3 vertices\n";
        return;
    }

    if(mCurGroup)
        mCurGroup->mPolygons.push_back(mPolygons.size()-1);
    if(mCurSmoothGroup)
        mCurSmoothGroup->mPolygons.push_back(mPolygons.size()-1);
    if(mCurMaterial)
        mCurMaterial->mPolygons.push_back(mPolygons.size()-1);

    std::vector<std::string> lV;

    for(int i=1;i<pV.size();i++)
    {
        split(pV[i],lV,fn::equal_to('/'));

        if(lV.empty()) continue;

        mVertices.mPosition.push_back(to_<int>(lV[0])-1);
        if(lV.size()>=2&&!lV[1].empty())
            mVertices.mTexCoord.push_back(to_<int>(lV[1])-1);
        else
            mVertices.mTexCoord.push_back(-1);
        if(lV.size()>=3&&!lV[2].empty())
            mVertices.mNormal.push_back(to_<int>(lV[2])-1);
        else
            mVertices.mNormal.push_back(-1);
    }

    mPolygons.push_back(mVertices.mPosition.size());
}

void COBJParser::ParseG(const std::vector<std::string> &pV)
{
    if(pV.size()<2)
    {
        msg_warn() << "malformed g tag\n";
        return;
    }

    for(int i=0;i<mGroups.size();i++)
    {
        if(mGroups[i]->mName==pV[1])
        {
            mCurGroup=mGroups[i];
            return;
        }
    }

    SGroup *lGroup=new SGroup;
    lGroup->mName=pV[1];
    mGroups.push_back(lGroup);
    mCurGroup=lGroup;
}

void COBJParser::ParseS(const std::vector<std::string> &pV)
{
    if(pV.size()<2)
    {
        msg_warn() << "malformed s tag\n";
        return;
    }

    if(pV[1]=="off"||pV[1]=="0")
    {
        mCurSmoothGroup=nullptr;
        return;
    }

    for(int i=0;i<mSmoothGroups.size();i++)
    {
        if(mSmoothGroups[i]->mName==pV[1])
        {
            mCurSmoothGroup=mSmoothGroups[i];
            return;
        }
    }

    SGroup *lGroup=new SGroup;
    lGroup->mName=pV[1];
    mSmoothGroups.push_back(lGroup);
    mCurSmoothGroup=lGroup;
}

void COBJParser::ParseUsemtl(const std::vector<std::string> &pV)
{
    if(pV.size()<2)
    {
        msg_warn() << "malformed usemtl tag\n";
        return;
    }

    for(int i=0;i<mMaterials.size();i++)
    {
        if(mMaterials[i]->mName==pV[1])
        {
            mCurMaterial=mMaterials[i];
            return;
        }
    }

    SGroup *lGroup=new SGroup;
    lGroup->mName=pV[1];
    mMaterials.push_back(lGroup);
    mCurMaterial=lGroup;
}

PMesh COBJParser::Parse(const io::CURI &pURL,double pScale)
{
    std::string lBuf;

    io::PBufferStream lS=io::open_buffered(pURL,io::EMode::READ);

    mPolygons.push_back(0);

    while(get_line(lS,lBuf))
    {
        std::vector<std::string> lV;

        split(lBuf,lV,fn::any_of(" \n"),nDeleteEmpty);

        if(lV.empty()) continue;

        if(lV[0][0]=='v')
        {
            ParseV(lV);
        }
        else if(lV[0]=="f")
        {
            ParseF(lV);
        }
        else if(lV[0]=="g")
        {
            ParseG(lV);
        }
        else if(lV[0]=="s")
        {
            ParseS(lV);
        }
        else if(lV[0]=="usemtl")
        {
            ParseUsemtl(lV);
        }
        else
        {
            msg_warn() << "unknown tag " << lV[0] << "\n";
        }
    }

    PMesh lMesh=new CMesh;

///\todo this with new format 
    /*
    lMesh->SetPosition(mPosition);
    lMesh->SetNormal(mNormal);
    lMesh->SetTexCoord(mTexCoord);

    lMesh->SetVertices(mVertices.mPosition,mVertices.mNormal,mVertices.mTexCoord);
    lMesh->SetFaces(mPolygons);

    for(int i=0;i<mGroups.size();i++)
    {
        lMesh->AddSubMesh(mGroups[i]->mName,mGroups[i]->mPolygons);
    }
*/
    return lMesh;
}

/*namespace import*/ } /*namespace scene*/ } /*namespace buola*/ }
